package owl2vowl.converter;

import java.util.Collection;
import java.util.Iterator;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassAxiom;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyAxiom;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.parameters.Imports;
import org.semanticweb.owlapi.search.EntitySearcher;
import org.semanticweb.owlapi.util.OWLOntologyWalker;

import owl2vowl.export.types.Exporter;
import owl2vowl.export.types.JsonGenerator;
import owl2vowl.model.data.VowlData;
import owl2vowl.model.entities.AbstractEntity;
import owl2vowl.model.ontology.OntologyMetric;
import owl2vowl.parser.owlapi.EntityCreationVisitor;
import owl2vowl.parser.owlapi.IndividualsVisitor;
import owl2vowl.parser.vowl.AnnotationParser;
import owl2vowl.parser.vowl.BaseIriCollector;
import owl2vowl.parser.vowl.EquivalentSorter;
import owl2vowl.parser.vowl.ImportedChecker;
import owl2vowl.parser.vowl.OntologyInformationParser;
import owl2vowl.parser.vowl.TypeSetter;
import owl2vowl.parser.vowl.classes.GenericClassAxiomVisitor;
import owl2vowl.parser.vowl.classes.HasKeyAxiomParser;
import owl2vowl.parser.vowl.classes.OwlClassAxiomVisitor;
import owl2vowl.parser.vowl.property.DataPropertyVisitor;
import owl2vowl.parser.vowl.property.DomainRangeFiller;
import owl2vowl.parser.vowl.property.ObjectPropertyVisitor;
import owl2vowl.parser.vowl.property.VowlSubclassPropertyGenerator;

public abstract class AbstractConverter implements Converter {
	private static final Logger logger = LogManager.getLogger(AbstractConverter.class);
	protected final JsonGenerator jsonGenerator = new JsonGenerator();
	protected String loadedOntologyPath;
	protected OWLOntologyManager manager;
	protected VowlData vowlData;
	protected OWLOntology ontology;
	protected boolean initialized = false;

	private void preLoadOntology() {
		try {
			this.loadOntology();
		} catch (OWLOntologyCreationException arg1) {
			throw new RuntimeException(arg1);
		}

		this.initialized = true;
	}

	protected abstract void loadOntology() throws OWLOntologyCreationException;

	private void preParsing(OWLOntology ontology, VowlData vowlData, OWLOntologyManager manager) {
		OWLOntologyWalker walker = new OWLOntologyWalker(ontology.getImportsClosure());
		EntityCreationVisitor ecv = new EntityCreationVisitor(vowlData);

		try {
			walker.walkStructure(ecv);
			logger.info("WalkStructure Success!");
		} catch (Exception arg6) {
			logger.info("@WORKAROUND WalkStructure Failed!");
			logger.info("Exception: " + arg6);
		}

		(new OntologyInformationParser(vowlData, ontology)).execute();
	}

	private void parsing(OWLOntology ontology, VowlData vowlData, OWLOntologyManager manager) {
		this.processClasses(ontology, vowlData);
		this.processObjectProperties(ontology, vowlData);
		this.processDataProperties(ontology, vowlData);
		this.processIndividuals(ontology, vowlData, manager);
		this.processGenericAxioms();
	}

	private void processIndividuals(OWLOntology ontology, VowlData vowlData, OWLOntologyManager manager) {
		ontology.getClassesInSignature(Imports.INCLUDED).forEach((owlClass) -> {
			Iterator arg2 = manager.getOntologies().iterator();

			while (arg2.hasNext()) {
				OWLOntology owlOntology = (OWLOntology) arg2.next();

				try {
					EntitySearcher.getIndividuals(owlClass, owlOntology).forEach((owlIndividual) -> {
						owlIndividual.accept(new IndividualsVisitor(vowlData, owlIndividual, owlClass, manager));
					});
				} catch (Exception arg5) {
					logger.info("@WORKAROUND: Failed to accept some individuals ... SKIPPING THIS");
					logger.info("Exception: " + arg5);
					logger.info("----------- Continue Process --------");
				}
			}

		});
	}

	private void processObjectProperties(OWLOntology ontology, VowlData vowlData) {
		Iterator arg2 = ontology.getObjectPropertiesInSignature(Imports.INCLUDED).iterator();

		while (arg2.hasNext()) {
			OWLObjectProperty owlObjectProperty = (OWLObjectProperty) arg2.next();
			Iterator arg4 = ontology.getAxioms(owlObjectProperty, Imports.INCLUDED).iterator();

			while (arg4.hasNext()) {
				OWLObjectPropertyAxiom owlObjectPropertyAxiom = (OWLObjectPropertyAxiom) arg4.next();

				try {
					owlObjectPropertyAxiom.accept(new ObjectPropertyVisitor(vowlData, owlObjectProperty));
				} catch (Exception arg7) {
					logger.info(
							"          @WORKAROUND: Failed to accept property with HAS_VALUE OR  SubObjectPropertyOf ... SKIPPING THIS");
					logger.info("          propertyName: " + owlObjectProperty);
					logger.info("          propertyAxiom: " + owlObjectPropertyAxiom);
				}
			}
		}

	}

	private void processDataProperties(OWLOntology ontology, VowlData vowlData) {
		Iterator arg2 = ontology.getDataPropertiesInSignature(Imports.INCLUDED).iterator();

		while (arg2.hasNext()) {
			OWLDataProperty property = (OWLDataProperty) arg2.next();
			Iterator arg4 = ontology.getAxioms(property, Imports.INCLUDED).iterator();

			while (arg4.hasNext()) {
				OWLDataPropertyAxiom propertyAxiom = (OWLDataPropertyAxiom) arg4.next();
				propertyAxiom.accept(new DataPropertyVisitor(vowlData, property));
			}
		}

	}

	private void createSubclassProperties(VowlData vowlData) {
		(new VowlSubclassPropertyGenerator(vowlData)).execute();
	}

	private void fillDomainRanges(VowlData vowlData) {
		(new DomainRangeFiller(vowlData, vowlData.getProperties())).execute();
	}

	private void processClasses(OWLOntology ontology, VowlData vowlData) {
		Iterator arg2 = ontology.getClassesInSignature(Imports.INCLUDED).iterator();

		while (arg2.hasNext()) {
			OWLClass owlClass = (OWLClass) arg2.next();
			Iterator arg4 = ontology.getAxioms(owlClass, Imports.INCLUDED).iterator();

			while (arg4.hasNext()) {
				OWLClassAxiom owlClassAxiom = (OWLClassAxiom) arg4.next();
				OwlClassAxiomVisitor temp = new OwlClassAxiomVisitor(vowlData, owlClass);

				try {
					owlClassAxiom.accept(temp);
				} catch (Exception arg8) {
					logger.info("ProcessClasses : Failed to accept owlClassAxiom -> Skipping");
				}
			}

			HasKeyAxiomParser.parse(ontology, owlClass, vowlData);
		}

	}

	private void processGenericAxioms() {
		this.ontology.getGeneralClassAxioms().forEach((owlClassAxiom) -> {
			owlClassAxiom.accept(new GenericClassAxiomVisitor(this.vowlData));
		});
	}

	private void parseAnnotations(VowlData vowlData, OWLOntologyManager manager) {
		AnnotationParser annotationParser = new AnnotationParser(vowlData, manager);
		annotationParser.parse();
	}

	private void setCorrectType(Collection<AbstractEntity> entities) {
		Iterator arg1 = entities.iterator();

		while (arg1.hasNext()) {
			AbstractEntity entity = (AbstractEntity) arg1.next();
			entity.accept(new TypeSetter());
		}

	}

	private void postParsing(OWLOntology loadedOntology, VowlData vowlData, OWLOntologyManager manager) {
		this.setCorrectType(vowlData.getEntityMap().values());
		this.parseAnnotations(vowlData, manager);
		this.fillDomainRanges(vowlData);
		this.createSubclassProperties(vowlData);
		(new ImportedChecker(vowlData, manager, loadedOntology, this.loadedOntologyPath)).execute();
		vowlData.getEntityMap().values().forEach((entity) -> {
			entity.accept(new EquivalentSorter(
					(IRI) this.ontology.getOntologyID().getOntologyIRI().orElse(IRI.create(this.loadedOntologyPath)),
					vowlData));
		});
		(new BaseIriCollector(vowlData)).execute();
	}

	public void convert() {
		if (!this.initialized) {
			this.preLoadOntology();
		}

		this.vowlData = new VowlData();
		this.vowlData.setOwlManager(this.manager);
		this.preParsing(this.ontology, this.vowlData, this.manager);
		this.parsing(this.ontology, this.vowlData, this.manager);
		this.postParsing(this.ontology, this.vowlData, this.manager);
		this.processMetrics();
	}

	private void processMetrics() {
		OntologyMetric metrics = new OntologyMetric(this.ontology);
		metrics.calculate(this.vowlData);
		this.vowlData.setMetrics(metrics);
	}

	public void export(Exporter exporter) throws Exception {
		if (this.vowlData == null) {
			this.convert();
		}

		this.jsonGenerator.execute(this.vowlData);
		this.jsonGenerator.export(exporter);
	}
}